package com.example.demo.common;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

@Aspect // 表明此类是一个切面
@Component // 不能省略
public class UserAOP {

    // 定义切点（也就是定义一个拦截规则 ---- 但不实现，实现是在“通知”里面实现）
    // 拦截的是：com.example.demo.controller 这个包下面的 UserController 类里边的任何方法的任何参数
    // UserController.* 表示的是 该类下的任何方法，方法里的任何参数用 *(..) 表示
    // execution(<修饰符><返回类型><包.类.⽅法(参数)><异常>)，修饰符可省略，返回类型不能省，包名前面的 * 为返回类型，异常 可省略
    @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
    public void pointcut() {
        // 只是配置了拦截规则，并不实现
    }

    // 前置通知：通知⽅法会在 ⽬标⽅法调⽤之前 执⾏
    @Before("pointcut()")
    public void doBefore() {
        System.out.println("执行了前置通知：" + LocalDateTime.now());
    }

    // 后置通知：通知⽅法会在⽬标⽅法返回或者抛出异常后调⽤
    @After("pointcut()")
    public void doAfter() {
        System.out.println("执行了后置通知：" + LocalDateTime.now());
    }

    // 环绕通知：通知包裹了被通知的⽅法，在被通知的⽅法通知之前和调⽤之后执⾏⾃定义的⾏为
    // 如果三种通知放在一起，那么环绕通知在最开始和最后执行，前置和后置在环绕通知的中间进行执行
    // 因为环绕通知需要在执行方法前后都要被调用，所以传入的参数是方法体
    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("开始执行环绕通知了");
        Object obj = joinPoint.proceed();  // 调用目标方法
        System.out.println("结束环绕通知了");
        return obj;
    }

}
